package com.example.order.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.example.order.pojo.Order;
import com.example.order.properties.OrderProperties;
import com.example.order.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * @RefreshScope 注解告诉 Spring，把这个 Bean 放到动态代理池里
 * 当 Nacos 推送配置变更时，Spring Context 重新注入属性（动态刷新）
 * 不用重启应用，配置立刻生效！
 */
//@RefreshScope
@Slf4j
@RestController
@RequestMapping
public class OrderController {

    @Autowired
    private OrderService orderService;

//    @Value("${order.timeout}")
//    String orderTimeout;
//    @Value("${order.auto-confirm}")
//    String orderAutoConfirm;

    @Autowired
    private OrderProperties orderProperties;

    /**
     * Nacos 配置文件参数
     */
    @GetMapping("/config")
    public String config() {
        return "order.timeout=" + orderProperties.getTimeout() + ";" +
                "order.auto-confirm=" + orderProperties.getAutoConfirm() + ";" +
                "order.db-url=" + orderProperties.getDbUrl();
    }

    /**
     * 创建订单
     */
    @GetMapping("/create")
    public Order createOrder(@RequestParam("productId") Long productId, @RequestParam("userId") Long userId) {
        Order order = orderService.createOrder(productId, userId);
        return order;
    }

    /**
     * 秒杀订单
     * 请求路径 /order/seckill 被调用：
     * 一次是在 Web 请求下（例如被浏览器直接访问）
     * 一次是内部方法调用（被其他方法或服务触发）
     * 那么它们会生成两个不同的上下文，如：
     * 上下文（Context）	            资源名	        解释
     * sentinel_spring_web_context	/order/seckill	Spring Web 框架生成的上下文
     * default_context 或某业务方法	/order/seckill	方法注解或其他调用链生成
     * 即使资源名相同，它们处于不同的调用链中，因此 分别监控与控制。
     */
    /**
     * 目前 Sentinel 自带的 adapter 仅 Dubbo 方法埋点带了热点参数，其它适配模块（如 Web）默认不支持热点规则，可通过自定义埋点方式指定新的资源名并传入希望的参数。注意自定义埋点的资源名不要和适配模块生成的资源名重复，否则会导致重复统计。
     */
    @GetMapping("/seckill")
    @SentinelResource(value = "seckill-order", fallback = "seckillFallback") // 自定义埋点方式指定新的资源名并传入希望的参数
    public Order seckill(@RequestParam(value = "productId", defaultValue = "1000") Long productId, @RequestParam(value = "userId", required = false) Long userId) {
        Order order = orderService.createOrder(productId, userId);
        order.setId(Long.MAX_VALUE);
        return order;
    }

    /**
     * 兜底回调
     */
    public Order seckillFallback(Long productId, Long userId, Throwable exception) {
        System.out.println("seckillFallback");
        Order order = new Order();
        order.setUserId(productId);
        order.setUserId(userId);
        order.setAddress("异常信息：" + exception.getClass());
        return order;
    }

    /**
     * 读操作
     * 流控模式-关联
     */
    @GetMapping("/writeDb")
    public String writeDb() {
        return "writeDB success";
    }

    /**
     * 写操作
     * 流控模式-关联
     */
    @GetMapping("/readDb")
    public String readDb() {
        log.info("readDb");
        return "readDB success";
    }

}